﻿#include "widget.h"
#include "ui_widget.h"

#include <algorithm>
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#include <QTimer>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <windows.h>

#include "playerwidget.h"
#include "videodealthread.h"
#include "videoitem.h"

const QString OUTPUT_DIR_FILENAME = "output_dir.xq";

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget), frame_count(0), time()
{
    ui->setupUi(this);
    setAcceptDrops(true);
    setWindowTitle("字符视频");

    // 获取本机有多少cpu，初始化cpu_state_map
    // 获取cpu核心数
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    cores_number = info.dwNumberOfProcessors;

    // 读取输出目录信息
    QFile file(OUTPUT_DIR_FILENAME);
    if (! file.exists())
    {
        qDebug() << "文件不存在，但已经创建";
        file.open(QIODevice::WriteOnly);
        file.close();
    }

    file.open(QIODevice::ReadOnly);
    output_dir = QString(file.readAll());
    file.close();
    ui->label->setText("输出目录 ->" + output_dir);

    // 更改输出目录按钮
    connect(ui->btn_change, &QPushButton::clicked, this, [this]()
    {
        output_dir = QFileDialog::getExistingDirectory(this, "选择新的输出路径", "/");
        if (output_dir.size() != 0)
        {
            QFile file(OUTPUT_DIR_FILENAME);
            file.open(QIODevice::WriteOnly);
            file.write(output_dir.toUtf8());
            file.close();
            ui->label->setText("输出目录 ->" + output_dir);
        }
    });

    // 播放已有按钮
    connect(ui->btn_play, &QPushButton::clicked, this, [this]()
    {
        QString filename = QFileDialog::getOpenFileName(this, "选择svd文字视频文件", output_dir, "*.svd");
        if (filename.size() == 0)
            return;
        PlayerWidget *player_widget = new PlayerWidget(filename);
        player_widget->show();
    });

    // 开始转换按钮
    connect(ui->btn_start, &QPushButton::clicked, this, [this]()
    {
        // 检查输出目录是否为空
        if (output_dir.size() == 0)
        {
            QMessageBox::warning(this, "警告", "还没指定输出目录");
            return;
        }

        // 检查要处理的视频列表是否为空
        if (video_item_list.size() == 0)
        {
            QMessageBox::information(this, "通知", "没有要处理的视频");
            return;
        }

        // 记录时间
        time.start();

        // 如果本机cpu核心数大于要处理的视频数，直接一个视频对应一个核心进行处理
        if (cores_number >= video_item_list.size())
        {
            unsigned long long core = 1;
            for (const auto &item : video_item_list)
            {
                create_video_thread(item, core);
                core <<= 1;
            }
        }
        else
        {
            // 给每个核心指定一个视频处理任务，核心处理完该视频或遇到错误，会自动开始处理下一个视频
            unsigned long long core = 1;
            for (qsizetype i = 0; i < cores_number; ++i)
            {
                create_video_thread(video_item_list.at(i), core);
                core <<= 1;
            }
        }
    });

    // 一个视频处理出错或结束，开始处理下一个视频
    auto signal = &Widget::start_next_thread;
    connect(this, signal, this, [this](unsigned long long core)
    {
        // 寻找下一个没处理的视频
        auto item_it = std::find_if(video_item_list.begin(), video_item_list.end(), [](VideoItem* item)
        {
            return item->get_state() == "等待处理";
        });

        // 如果没找到还没处理的视频，就退出
        if (item_it == video_item_list.end())
        {
            qDebug() << "耗时 ->" << time.elapsed();
        }
        else // 创建线程并开始处理
        {
            create_video_thread(*item_it, core);
        }
    });
}

void Widget::create_video_thread(VideoItem *item, unsigned long long core)
{
    item->set_state("正在转换");

    // 处理视频的线程
    auto thread = new VideoDealThread(item->get_absolute_path(), output_dir, core);
    connect(thread, &VideoDealThread::file_empty_error, this, [this, item, thread]()
    {
        // 设置这一行的状态label
        item->set_state("无文件名");

        // 发送开始下一个线程的信号
        emit start_next_thread(thread->get_core());
    });

    auto signal_1 = &VideoDealThread::file_not_exist_error;
    connect(thread, signal_1, this, [this, item, thread](QString filename)
    {
        // 设置这一行的状态label
        qDebug() << "文件错误，文件名 ->" << filename;
        item->set_state("文件错误");

        // 发送开始下一个线程的信号
        emit start_next_thread(thread->get_core());
    });

    connect(thread, &VideoDealThread::deal_ok, this, [this, item, thread]()
    {
        item->set_state("处理完成");
        item->show_btn();

        // 获取核心，防止下面删除thread之后获取不到核心
        auto core = thread->get_core();

        // 删除thread
        thread->quit();

        // 移除
        video_item_list.removeOne(item);

        // 发送开始下一个线程的信号
        emit start_next_thread(core);
    });

    auto signal_2 = &VideoDealThread::change_jin_du;
    connect(thread, signal_2, this, [item](int new_jin_du)
    {
        item->set_progress(new_jin_du);
    });

    thread->start();
}

void Widget::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();    // 接收拖拽信号
}

void Widget::dropEvent(QDropEvent *event)
{
    // 检测是否拖到了虚线框里面
    auto pos = event->position();
    if ((pos.x() > ui->scrollArea->x()) &&
            (pos.x() < ui->scrollArea->x() + ui->scrollArea->width()) &&
            (pos.y() > ui->scrollArea->y()) &&
            (pos.y() < ui->scrollArea->y() + ui->scrollArea->height()))
    {
        ui->label_2->hide();
        for (const auto &url : event->mimeData()->urls())
        {
            // 查找是否已经存在该文件
            bool flag = false;
            for (const auto &item : video_item_list)
            {
                qDebug() << item->get_absolute_path() << "  " << url.toLocalFile();
                if (item->get_absolute_path() == url.toLocalFile())
                    flag = true;
            }
            if (flag)
                continue;

            VideoItem *item = new VideoItem(url);
            // 链接item里的播放按钮的点击事件
            connect(item, &VideoItem::btn_clicked, this, [this, item]()
            {
                QString svd_filename = output_dir + "/" + item->get_filename().section(".", 0, 0) + ".svd";
                PlayerWidget *player_widget = new PlayerWidget(svd_filename);
                player_widget->show();
            });

            ui->verticalLayout_main->addWidget(item);
            video_item_list.push_back(item);
        }
    }
}

Widget::~Widget()
{
    delete ui;
}

